package com.github.shiqiyue.app;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.shiqiyue.app.common.enums.IBaseEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

/***
 * 前端枚举生成器
 * @author wenyao.wu
 */
@Slf4j
public class FrontEnumGeneratorPlus {


    public static void main(String[] args) throws IOException {
        ClassScanner classUtil = new ClassScanner(IBaseEnum.class, "com.github.shiqiyue.app");
        List<Class<? extends IBaseEnum>> classList = new ArrayList<>();
        for (Class item : classUtil.getAllClassByPackage()) {
            classList.add(item);
        }
        String sourceLocation = System.getProperty("user.dir") + "/commons/src/main/java/";
        String location = "D:\\generator\\" + DateFormatUtils.format(new Date(), "yyyyMMddhhmmss") + "\\";
        FrontEnumGeneratorPlus generator = new FrontEnumGeneratorPlus(classList, location, sourceLocation, true);
        generator.generate();
        Runtime.getRuntime().exec("explorer " + location);
    }


    /***
     * 要生成的枚举
     */
    private List<Class<? extends IBaseEnum>> enumList;

    /***
     * 生成文件放置的目录
     */
    private String generatorLocation;

    /***
     * 源码目录
     */
    private String sourceLocation;

    /***
     * 生成后是否打开文件夹
     */
    private boolean open = true;

    public FrontEnumGeneratorPlus(List<Class<? extends IBaseEnum>> enumList, String generatorLocation, String sourceLocation, boolean open) {
        this.enumList = enumList;
        this.generatorLocation = generatorLocation;
        this.open = open;
        this.sourceLocation = sourceLocation;
    }

    public void generate() throws IOException {
        if (enumList == null || enumList.size() == 0) {
            log.warn("没有枚举需要生产");
            return;
        }
        for (Class<? extends IBaseEnum> item : enumList) {
            if (!item.isEnum()) {
                continue;
            }
            IBaseEnum[] array = item.getEnumConstants();
            Map<String, Dict> dicts = convertEnumValueArrayToDictMap(array);

            String json = JSONObject.toJSONString(dicts, SerializerFeature.UseSingleQuotes, SerializerFeature.MapSortField);
            json = json.replace(":", ": ")
                    .replace(",", ", ")
                    .replace("}, ", "},\n  ")
                    .replace("{'", "{ '")
                    .replace("},", " },")
                    .replace("}}", " }}");
            String javaDoc = getJavaDoc(item);
            javaDoc = javaDoc.replace("***", "** *");
            StringBuilder stringBuilder = new StringBuilder(javaDoc)
                    .append("\n")
                    .append("const enums = ")
                    .append(json).append("\n")
                    .append("export default enums")
                    .append("\n");
            try {
                ouputToFile(generatorLocation, toLowerCaseFirstOne(item.getSimpleName() + ".js"), stringBuilder.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 首字母转小写
     *
     * @param s
     * @return
     */
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        }
        return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    private String getJavaDoc(Class c) throws IOException {
        try {
            String fileName = sourceLocation + c.getCanonicalName().replace(".", "/") + ".java";
            String content = FileUtils.readFileToString(new File(fileName));
            content = content.substring(content.indexOf("/*"), content.indexOf("*/") + 2);
            return content;
        } catch (Exception e) {
            return "";
        }

    }

    public void ouputToFile(String dirPath, String filename, String content) throws IOException {
        File dir = new File(dirPath);
        FileUtils.forceMkdir(dir);
        File file = new File(dir, filename);
        FileUtils.write(file, content);
    }

    public Map<String, Dict> convertEnumValueArrayToDictMap(IBaseEnum[] enumValues) {
        Map<String, Dict> dictList = new HashMap<>(8);
        if (enumValues != null && enumValues.length > 0) {
            for (IBaseEnum enumValue : enumValues) {
                Enum e = (Enum) enumValue;
                Dict dict = new Dict();
                dict.setText(enumValue.getName());
                dict.setValue(enumValue.getValue());
                dictList.put(e.name(), dict);
            }
        }
        return dictList;
    }

    public static class Dict {
        private String text;

        private Integer value;

        public Dict() {
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public Integer getValue() {
            return value;
        }

        public void setValue(Integer value) {
            this.value = value;
        }
    }


    /**
     * 通过接口获取所有实现
     *
     * @author http://anxpp.com/ 时间：2016年8月23日 下午10:16:53
     */
    @SuppressWarnings("rawtypes")
    public static class ClassScanner {

        private Class clazz;
        private String packagePath;

        public ClassScanner(Class clazz, String packagePath) {
            this.clazz = clazz;
            this.packagePath = packagePath;
        }

        @SuppressWarnings("unchecked")
        public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {

        }

        /**
         * 通过包名获取所有实现（可以将包名配置到统一配置文件中）
         *
         * @param
         * @return
         */
        @SuppressWarnings("unchecked")
        public List<Class> getAllClassByPackage() {
            ArrayList<Class> returnClassList = new ArrayList<Class>();
            try {
                List<Class> allClass = getClasses(packagePath);
                // 判断是否是一个接口
                for (int i = 0; i < allClass.size(); i++) {
                    if (clazz.isAssignableFrom(allClass.get(i))) {
                        if (!clazz.equals(allClass.get(i))) {
                            returnClassList.add(allClass.get(i));
                        }
                    }
                }
            } catch (Exception e) {
            }
            return returnClassList;
        }

        private List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace(".", "/");
            Enumeration<URL> resources = classLoader.getResources(path);
            List<File> dirs = new ArrayList<File>();
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                dirs.add(new File(resource.getFile()));
            }
            ArrayList<Class> classes = new ArrayList<Class>();
            for (File directory : dirs) {
                classes.addAll((Collection<? extends Class>) findClass(directory, packageName));
            }
            return classes;
        }

        private List<Class> findClass(File directory, String packageName) throws ClassNotFoundException {
            List<Class> classes = new ArrayList<Class>();
            if (!directory.exists()) {
                return classes;
            }
            File[] files = directory.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    classes.addAll(findClass(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    classes.add((Class) Class.forName(packageName + "." + file.getName().substring(0, file.getName().length() - 6)));
                }
            }
            return classes;
        }
    }


}
